package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"runtime"
	"sync"
	"time"
)

/**
 go的执行是非阻塞的，不会等待
go后面的
*/
func main() {

	//test1()
	//go test2()
	//test3()
	//test4()
	//test5()
	//test6()
	//test7()
	//test8()
	//test10()
	//test11()
	test12()

}

func test12() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}

	time.Sleep(6 * time.Second)
}

func server1(ch chan string) {
	time.Sleep(3 * time.Second)
	ch <- "from server1"
	//defer wg.Done()
}

func server2(ch chan string) {
	time.Sleep(1 * time.Second)
	ch <- "from server2"
	//defer wg.Done()
}

type Job struct {
	id       int
	randomno int
}

type Result struct {
	job         Job
	sumofdigits int
}

var jobs = make(chan Job, 10)
var results = make(chan Result, 10)
var wg sync.WaitGroup

func test11() {

	startTime := time.Now()
	noOfJobs := 10

	//创建10个jobs
	go allocate(noOfJobs)

	done := make(chan bool)

	//输出结果，成功写入done通道
	go result(done)

	//创建工作池
	noOfWorkers := 2
	createWorkerPool(noOfWorkers)

	<-done
	endTime := time.Now()
	diff := endTime.Sub(startTime)
	fmt.Println("total time taken ", diff.Seconds(), "seconds")

}

func digits(number int) int {
	sum := 0
	no := number

	for no != 0 {
		digit := no % 10
		sum += digit
		no /= 10
	}
	time.Sleep(2 * time.Second)
	return sum
}

func worker() {
	for job := range jobs {
		output := Result{job, digits(job.randomno)}
		results <- output
	}
	wg.Done()
}

func createWorkerPool(noOfWorkers int) {
	for i := 0; i < noOfWorkers; i++ {
		wg.Add(1)
		go worker()
	}
	wg.Wait()
	close(results)
}

func allocate(noOfJobs int) {
	for i := 0; i < noOfJobs; i++ {
		randomno := rand.Intn(999)
		job := Job{i, randomno}
		jobs <- job
	}
	close(jobs)
}

func result(done chan bool) {
	for result := range results {
		fmt.Printf("Job id %d, input random no %d , sum of digits %d\n", result.job.id, result.job.randomno, result.sumofdigits)
	}
	done <- true
}

/*
write 协程在并发地运行。write 协程有一个 for 循环，
依次向信道 ch 写入 0～4。而缓冲信道的容量为 2，
因此 write 协程里立即会向 ch 写入 0 和 1，
接下来发生阻塞，直到 ch 内的值被读取。
*/
func write(ch chan int) {
	defer close(ch)
	for i := 0; i < 10; i++ {
		ch <- i
		fmt.Printf("write %d to ch\n", i)
	}
}

func test10() {
	ch := make(chan int, 2)
	go write(ch)

	for v := range ch {
		fmt.Printf("read value %d from ch \n", v)
		time.Sleep(2 * time.Second)
	}
	wg.Done()
}

func test9() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	//ch <- 3  往满的通道写数据会导致死锁

	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

func test8() {
	var chnl = make(chan int)
	go producter(chnl)

	for {
		v, ok := <-chnl
		if ok == false {
			break
		}
		fmt.Println("received", v, ok)
	}
}

func producter(chnl chan int) {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		chnl <- i
	}
	close(chnl)
}

func test7() {
	sendch := make(chan int)
	go sendData(sendch)
	fmt.Println(<-sendch)
}

func sendData(sendch chan<- int) {
	sendch <- 10
	//fmt.Println(<-sendch) 只能写数据不能读
}

func readData(sendch <-chan int) {
	data := <-sendch
	fmt.Println(data)
	//sendch <- 10 只能读数据不能写
}

func test6() {
	ch := make(chan int)
	ch <- 5
}

func test5() {
	ch := make(chan int, 1)
	go func(chan int) {
		for {
			select {
			case ch <- 0:
			case ch <- -1:
			}
		}
	}(ch)

	for i := 0; i < 10; i++ {
		fmt.Println(<-ch)

	}
}

func test4() {
	var urls = []string{
		"http://www.golang.org/",
		"http://www.baidu.com/",
		"http://www.google.com/",
		"http://www.qq.com/",
	}
	var wg sync.WaitGroup

	for _, url := range urls {

		wg.Add(1)

		go func(url string) {
			defer wg.Done()

			resp, err := http.Get(url)
			if err == nil {
				fmt.Println(resp.Status)
			} else {
				fmt.Println(err.Error())
			}
		}(url)

	}

	wg.Wait()

}

func test3() {
	c := make(chan int)

	go func() {
		sum := 0
		for i := 0; i < 100; i++ {
			sum += i
		}

		println(sum)

		//写通道
		c <- sum
	}()

	println("NumGoroutine", runtime.NumGoroutine())

	sum := <-c
	println(sum)
}

func test2() {
	sum := 0

	for i := 0; i < 100; i++ {
		sum += i
	}
	println(sum)
	time.Sleep(1 * time.Second)

	println("NumGoroutine", runtime.NumGoroutine())

	time.Sleep(5 * time.Second)
}

func test1() {
	go func() {
		sum := 0

		for i := 0; i < 100; i++ {
			sum += i
		}
		println(sum)
		time.Sleep(1 * time.Second)
	}()

	println("NumGoroutine", runtime.NumGoroutine())

	time.Sleep(5 * time.Second)
}
